unit oStatisticProcessor;

// Miro Rusin 30.5.10
// class to process statistics from simulation

interface

uses Generics.Collections, Classes, oSerializer, SysUtils,
  oStatisticEventConstants, oIStoreable, XMLINTf, oStats, oEntityTypeConstants,
  XMLDoc;

type

  EStatisticProcessor = class(Exception);

  TStringIntegerPair = class
     aKey : string;
     aValue : integer;
    public
      constructor Create; overload;
      constructor Create(paKey : string; paValue : integer); overload;
      property Key : string read aKey write aKey;
      property Value : integer read aValue write aValue;
  end;

  TStatistic = class(TInterfacedObject, IStoreable)
   private
    aMean, aDispersion : double;
   public
    property Mean : double read aMean write aMean;
    property Dispersion : double read aDispersion write aDispersion;
    procedure SaveToXML(var paNode : Ixmlnode);
    procedure LoadFromXML(paNode : IXMLNode);
    constructor Create;
  end;

  TIntegerSimpleStatisticPair = class
      aKey : integer;
      aValue : TSimpleStat;
    public
      property Key : integer read aKey write aKey;
      property Value : TSimpleStat read aValue write aValue;
      constructor Create;
      destructor Destroy; override;
  end;

  TIntegerStatisticPair = class
      aKey : integer;
      aValue : TStatistic;
    public
      property Key : integer read aKey write aKey;
      property Value : TStatistic read aValue write aValue;
      constructor Create;
      destructor Destroy; override;
      procedure SaveToXML(var paNode : Ixmlnode);
      procedure LoadFromXML(paNode : IXMLNode);
  end;

  TStringSimpleStatPair = class
      aKey : string;
      aValue : TSimpleStat;
    public
      property Key : string read aKey write aKey;
      property Value : TSimpleStat read aValue write aValue;
      constructor Create;
      destructor Destroy; override;
  end;

  TStringStatisticPair = class(TInterfacedObject, IStoreable)
      aKey : string;
      aValue : TStatistic;
    public
      property Key : string read aKey write aKey;
      property Value : TStatistic read aValue write aValue;
      constructor Create; overload;
      constructor Create(paKey : string); overload;
      procedure SaveToXML(var paNode : Ixmlnode);
      procedure LoadFromXML(paNode : IXMLNode);
      destructor Destroy; override;
  end;

  TReplicationStatistic = class
    private
     aWaitingTimes : TList<TStringStatisticPair>;
     aArrivalCounts : TList<TStringStatisticPair>;
    public
     property WaitingTimes : TList<TStringStatisticPair> read aWaitingTimes;
     property ArrivalCounts : TList<TStringStatisticPair> read aArrivalCounts;
     constructor Create;
     procedure Clear;
     destructor Destroy; override;
     procedure SaveToXML(var paNode : Ixmlnode);
     procedure LoadFromXML(paNode : IXMLNode);
  end;

   TStatisticProcessor = class
    private
     aPath : string;
     aRepCount, aStatType, aStatGroup, aReplication, aObjectId : integer;

     aRepStats : Tlist<TReplicationStatistic>;
     aWaitingTimesResult : TList<TStringStatisticPair>;
     aPeopleInModel : TSimpleStat;
     aPeopleInModelResult : TStatistic;
     aArrivalCountsResults : TList<TStringStatisticPair>;
     aChoice : TStringList;
     aIntChoice : Tlist<integer>;
     class var aid : integer;
     class var aTypes, aObjects : TList<TStringIntegerPair>;
     procedure processStatistic(paType, paId : integer;
      paFileName : string);
     procedure addToIntList(paList : TList<TStringSimpleStatPair>;
      paStrings : TStringList);
     procedure addToStringList(paList : TList<TStringStatisticPair>; paStrings : TStringList);
     procedure processResults;
     procedure deleteFiles;
     procedure clearStructures;
    public
     class function getStatisticGroups : TList<TStringIntegerPair>;
     class function getStatisticTypes : TList<TStringIntegerPair>;

     procedure LoadFromXML(paPath : string);
     procedure SaveToXML(paPath : string);

     procedure LoadFromCSV(paPath : string);
     procedure SaveToCSV(paPath : string);

     class property SimulationId : integer read aId write aId;
     property IntChoice : TList<integer> read aIntChoice write aIntChoice;
     property Path : string read aPath write aPath;
     property ReplicationCount : integer read aRepCount write aRepCount;
     property StatisticGroup : integer read aStatGroup write aStatGroup;
     property Replication : integer read aReplication write aReplication;
     property ReplicationStats : TList<TReplicationStatistic> read aRepStats;
     property ObjectId : integer read aObjectId write aObjectId;
     property WaitingTimesResult : TList<TStringStatisticPair> read aWaitingTimesResult;
     property PeopleInModelResult : TStatistic read aPeopleInModelResult;
     property PeopleInModel : TSimpleStat read aPeopleInModel;
     property ArrivalCountsResults : TList<TStringStatisticPair> read aArrivalCountsResults;
     property StringChoice : TStringList read aChoice write aChoice;
     procedure processStatistics(paStatisticFlag : Tlist<Boolean>);
     constructor Create;
     destructor Destroy; override;
   end;

implementation

{ TStatisticProcessor }

procedure TStatisticProcessor.addToStringList(paList: TList<TStringStatisticPair>;
  paStrings: TStringList);
var
  map : TStringStatisticPair;
  match : boolean;
begin
  match := false;
  for map in paList do
  begin
     if map.Key = paStrings[0] then
      begin
        map.Value.Mean := StrToFloat(paStrings[1]);
        match := true;
      end;
  end;
  if not match then
    begin
      map := TStringStatisticPair.Create;
      map.Key := paStrings[0];
      map.Value.Mean := StrToFloat(paStrings[1]);
      paList.Add(map);
    end;
end;

procedure TStatisticProcessor.addToIntList(paList : TList<TStringSimpleStatPair>;
  paStrings : TStringList);
var
  map : TStringSimpleStatPair;
  match : boolean;
begin
  match := false;
  for map in paList do
  begin
     if map.Key = paStrings[0] then
      begin
        map.Value.AddVal(StrToFloat(paStrings[1]));
        match := true;
      end;
  end;
  if not match then
    begin
      map := TStringSimpleStatPair.Create;
      map.Key := (paStrings[0]);
      map.Value.AddVal(StrToFloat(paStrings[1]));
      paList.Add(map);
    end;
end;


procedure TStatisticProcessor.clearStructures;
begin
  aArrivalCountsResults.Clear;
  aWaitingTimesResult.Clear;
  aPeopleInModel.Reset;
  aRepStats.Clear;
end;

constructor TStatisticProcessor.Create;
begin
  aPath := '';
  aRepCount := 0;
  aRepStats := TList<TREplicationStatistic>.Create;
  aPeopleInModel := TSimpleStat.create;
  aArrivalCountsResults := Tlist<TStringStatisticPair>.Create;
  aPeopleInModelResult := TStatistic.create;
  aChoice := TStringList.Create;
  aWaitingTimesResult := TList<TStringStatisticPair>.create;
end;

procedure TStatisticProcessor.deleteFiles;
var
  f : TextFile;
  j : integer;
  i: Integer;
  sr : TSearchRec;
begin
  for j := 1 to aRepCount do
    begin
      if FindFirst(aPath + '\replication' + inttoStr(j) +
            '\*.*', faAnyFile, SR) = 0 then
        repeat
         DeleteFile(aPath + '\replication' + inttoStr(j) + '\' + sr.Name);
        until FindNext(SR) <> 0;

     RmDir(aPath + '\replication' + inttoStr(j));
    end;
end;

destructor TStatisticProcessor.Destroy;
begin
  //deleteFiles;
  aRepStats.Free;
  aChoice.Free;
  aArrivalCountsResults.Free;
  aWaitingTimesResult.Free;
  aPeopleInModel.Free;
  aPeopleInModelResult.Free;
end;

procedure TStatisticProcessor.LoadFromCSV(paPath: string);
begin

end;

procedure TStatisticProcessor.processResults;
var
  map, map2: TStringStatisticPair;
  stat : TStringStatisticPair;
  statistic : TStatistic;
  j, i: Integer;
  simplstat : TSimpleStat;
  stats : TList<TSimpleStat>;
begin

  for map in aRepStats[0].aWaitingTimes do
   begin
     map2 := TStringStatisticPair.Create;
     map2.Key := map.aKey;
     aWaitingTimesResult.Add(map2);
   end;

  for j := 0 to aRepStats.Count - 1 do
   for i:= 0 to aRepStats[j].aWaitingTimes.Count - 1 do
    begin
      aWaitingTimesResult[i].Value.Mean := aWaitingTimesResult[i].Value.Mean +
          aRepStats[j].aWaitingTimes[i].Value.Mean / ReplicationCount;
      aWaitingTimesResult[i].Value.Dispersion := aWaitingTimesResult[i].Value.Dispersion +
          aRepStats[j].aWaitingTimes[i].Value.Dispersion / ReplicationCount;
    end;


    aPeopleInModelResult.Mean := aPeopleInModel.Mean;
    aPeopleInModelResult.Dispersion := aPeopleInModel.Dispersion;

  simplstat := TSimpleStat.create;
  stats := TList<TsimpleStat>.Create;

  for map in aRepStats[0].aArrivalCounts do
    begin
     stat := TStringStatisticPair.Create(map.Key);
     aArrivalCountsResults.Add(stat);
     stats.Add(TSimpleStat.create);
    end;


  for j := 0 to aRepStats.Count - 1 do
    for i := 0 to aRepStats[j].aArrivalCounts.Count - 1 do
      stats[i].AddVal(aRepStats[j].aArrivalCounts[i].Value.Mean);


  for j := 0 to aRepStats.Count - 1 do
    for i := 0 to aRepStats[j].aArrivalCounts.Count - 1 do
      begin
        aArrivalCountsResults[i].aValue.Mean := stats[i].Mean;
        aArrivalCountsResults[i].aValue.Dispersion := stats[i].Dispersion;
      end;
end;

procedure TStatisticProcessor.processStatistic(paType, paId : integer;
  paFileName : string);
var
  line : string;
  sList : TStringList;
  f: TextFile;
  j: Integer;
  res : TStatistic;
  waitings : Tlist<TStringSimpleStatPair>;
  map: TStringStatisticPair;
  repstat : TReplicationStatistic;

begin
  slist := TStringList.Create;
  slist.Delimiter := ':';
  AssignFile(f, aPath + '\replication' + inttoStr(paId) + '\' +
          paFileName + '.txt');
  Reset(f);
  waitings := Tlist<TStringSimpleStatPair>.Create;

  while not eof(f) do
     begin
        Readln(f, line);
        slist.DelimitedText := line;
        if slist.Count > 0 then
          begin
           case paType of
            SE_WAITING_TIME : begin
              addToIntList(waitings, sList);
            end;
            SE_PEOPLE_IN_MODEL : begin
              aPeopleInModel.AddVal(StrToFloat(slist[0]));
            end;
            SE_PEOPLE_IN_ARRIVAL : begin
              addToStringList(aRepStats[paId - 1].aArrivalCounts, sList);
            end;
           end;

          end;
     end;
    CloseFile(f);

    case paType of

      SE_WAITING_TIME : begin
        for j := 0 to waitings.Count - 1 do
        begin
          map := TStringStatisticPair.Create;
          map.Key := waitings[j].Key;
          map.Value.Mean := waitings[j].Value.Mean;
          map.Value.Dispersion := waitings[j].Value.Dispersion;
          aRepStats[paId - 1].aWaitingTimes.Add(map);
        end;
      end;

   end;
  waitings.Free;
end;

procedure TStatisticProcessor.processStatistics(paStatisticFlag: Tlist<Boolean>);
var
  j: Integer;
  i: Integer;
begin

  clearStructures;
  for j := 1 to aRepCount do
    begin
      aRepStats.Add(TReplicationStatistic.Create);
      for i := 0 to getStatisticTypes.Count - 1 do
      if (paStatisticFlag[i]) then
        processStatistic(i, j, getStatisticTypes[i].Key);
    end;

  processResults;

end;

class function TStatisticProcessor.getStatisticGroups: TList<TStringIntegerPair>;
begin
  if aObjects = nil then
   begin
    aObjects := TList<TStringIntegerPair>.Create;
    aObjects.Add(TStringIntegerPair.Create('pedestrians', ET_PEDESTRIAN));
    aObjects.Add(TStringIntegerPair.Create('cashiers', ET_CASHIER_TYPE));
    aObjects.Add(TStringIntegerPair.Create('doors', ET_DOOR_TYPE));
    aObjects.Add(TStringIntegerPair.Create('lifts', ET_LIFT_TYPE));
    aObjects.Add(TStringIntegerPair.Create('platforms', ET_PLATFORM_TYPE));
    aObjects.Add(TStringIntegerPair.Create('spawns', ET_SPAWN_TYPE));
    aObjects.Add(TStringIntegerPair.Create('stairs', ET_STAIRS_TYPE));
    aObjects.Add(TStringIntegerPair.Create('tables', ET_TABLE_TYPE));
    aObjects.Add(TStringIntegerPair.Create('turniquet', ET_TURNIQUET_TYPE));
    aObjects.Add(TStringIntegerPair.Create('obligatory waypoints', ET_OBLIGATORY_WAY_POINT));
    aObjects.Add(TStringIntegerPair.Create('optional waypoints', ET_OPTIONAL_WAY_POINT));
    aObjects.Add(TStringIntegerPair.Create('sinks', ET_SINK_TYPE));
    aObjects.Add(TStringIntegerPair.Create('walls', ET_WALL_TYPE));
    aObjects.Add(TStringIntegerPair.Create('waypoints', ET_WAYPOINT_TYPE));
   end;

  Result := aObjects;
end;

class function TStatisticProcessor.getStatisticTypes : TList<TStringIntegerPair>;
begin
  if aTypes = nil then
   begin
    aTypes := TList<TStringIntegerPair>.Create;
    aTypes.Add(TStringIntegerPair.Create('waiting time', SE_WAITING_TIME_RESULT));
    aTypes.Add(TStringIntegerPair.Create('people in model', SE_PEOPLE_IN_MODEL_RESULT));
    aTypes.Add(TStringIntegerPair.Create('people in arrival', SE_PEOPLE_IN_ARRIVAL_RESULT));
   end;

  Result := aTypes;
end;


procedure TStatisticProcessor.SaveToCSV(paPath: string);
begin

end;

procedure TStatisticProcessor.LoadFromXML(paPath: string);
var
  xml : iXMLDocument;
  root, node : IXMLNode;
  j: Integer;
  strStat : TStringStatisticPair;
  stat : TStatistic;
  repStat : TReplicationStatistic;
begin
  if FileExists(paPath) then
    begin
      xml := TXMLDocument.Create(nil);
      xml.XML := TSerializer.getStrings(paPath);
      xml.Active := true;
      root := xml.DocumentElement;

      TSerializer.loadValueFromXML(root, 'rep_count', aRepCount);

      clearStructures;

      node := root.ChildNodes['waiting_results'];
      for j := 0 to node.ChildNodes.Count - 1 do
        begin
          strStat := TStringStatisticPair.Create;
          strStat.LoadFromXML(node.ChildNodes[j]);
          aWaitingTimesResult.Add(strStat);
        end;


      node := root.ChildNodes['people_in_model_results'];
      aPeopleInModelResult.LoadFromXML(node);

      node := root.ChildNodes['people_in_model'];
      for j := 0 to node.ChildNodes.Count - 1 do
        begin
          stat := TStatistic.Create;
          stat.LoadFromXML(node.ChildNodes[j]);
          aPeopleInModel.AddVal(stat.Mean);
        end;


      node := root.ChildNodes['arrivals_results'];
      for j := 0 to node.ChildNodes.Count - 1 do
       begin
        strStat := TStringStatisticPair.Create;
        strStat.LoadFromXML(node.ChildNodes[j]);
        aArrivalCountsResults.Add(strStat);
       end;

      node := root.ChildNodes['replications'];
      for j := 0 to node.ChildNodes.Count - 1 do
       begin
        repStat := TReplicationStatistic.Create;
        repStat.LoadFromXML(node.ChildNodes[j]);
        aRepStats.Add(repStat);
       end;
    end
  else
   raise EStatisticProcessor.Create('No file to load');
end;

procedure TStatisticProcessor.SaveToXML(paPath : string);
var
  xml : IXMLDocument;
  root, node, n : IXMLNode;
  j: Integer;
  stat : TStatistic;
begin

  xml := TSerializer.prepareXMLDoc;
  xml.Active := true;
  root := xml.AddChild('data');
  TSerializer.saveValueToXML(root, 'rep_count', aRepCount);

  stat := TStatistic.Create;
  node := root.AddChild('people_in_model');

  for j := 0 to aPeopleInModel.Values.Count - 1 do
   begin
    n := node.AddChild('replication');
    stat.Mean := aPeopleInModel.Values[j];
    stat.SaveToXML(n);
   end;

  node := root.AddChild('people_in_model_results');
  aPeopleInModelResult.SaveToXML(node);

  node := root.AddChild('waiting_results');
  for j := 0 to aWaitingTimesResult.Count - 1 do
    aWaitingTimesResult[j].SaveToXML(node);

  node := root.AddChild('arrivals_results');
  for j := 0 to aArrivalCountsResults.Count - 1 do
    aArrivalCountsResults[j].SaveToXML(node);


  node := root.AddChild('replications');
  for j := 0 to aRepStats.Count - 1 do
    aRepStats[j].SaveToXML(node);

  xml.SaveToFile(paPath + '.stx');
end;

{ TStatistic }

constructor TStatistic.Create;
begin
  aMean := 0;
  aDispersion := 0;
end;

procedure TStatistic.LoadFromXML(paNode: IXMLNode);
begin
  TSerializer.loadValueFromXML(paNode.ChildNodes['statistic'], 'mean', aMean);
  TSerializer.loadValueFromXML(paNode.ChildNodes['statistic'], 'dispersion', aDispersion);
end;

procedure TStatistic.SaveToXML(var paNode: Ixmlnode);
var
  node : IXMLNode;
begin
  node := paNode.AddChild('statistic');
  TSerializer.saveValueToXML(node, 'mean', aMean);
  TSerializer.saveValueToXML(node, 'dispersion', aDispersion);
end;

{ TStringStatPair }

constructor TStringStatisticPair.Create;
begin
  aKey := '';
  aValue := TStatistic.create;
end;

constructor TStringStatisticPair.Create(paKey: string);
begin
  Create;
  aKey := paKey;
end;

destructor TStringStatisticPair.Destroy;
begin
  aValue.Free;
  inherited;
end;

procedure TStringStatisticPair.LoadFromXML(paNode: IXMLNode);
begin
  TSerializer.loadValueFromXML(paNode, 'key', aKey);

  TSerializer.loadValueFromXML(paNode.ChildNodes['value'], 'mean', aValue.aMean);
  TSerializer.loadValueFromXML(paNode.ChildNodes['value'], 'dispersion',
        aValue.aDispersion);
end;

procedure TStringStatisticPair.SaveToXML(var paNode: Ixmlnode);
var
  n1, node : IXMLNode;
begin
  n1 := paNode.AddChild('statistic');
  TSerializer.saveValueToXML(n1, 'key', aKey);

  node := n1.AddChild('value');
  TSerializer.saveValueToXML(node, 'mean', aValue.Mean);
  TSerializer.saveValueToXML(node, 'dispersion', aValue.Dispersion);
end;

{ TStringSimpleStatPair }

constructor TStringSimpleStatPair.Create;
begin
  aKey := '';
  aValue := TSimpleStat.create;
end;

destructor TStringSimpleStatPair.Destroy;
begin
  aValue.Free;
  inherited;
end;

{ TStringIntegerPair }

constructor TStringIntegerPair.Create;
begin

end;

constructor TStringIntegerPair.Create(paKey: string; paValue: integer);
begin
  Create;
  aKey := paKey;
  aValue := paValue;
end;

{ TIntegerStatisticPair }


constructor TIntegerStatisticPair.Create;
begin
  aKey := 0;
  aValue := TStatistic.Create;
end;

destructor TIntegerStatisticPair.Destroy;
begin
  aValue.Free;
end;

constructor TReplicationStatistic.Create;
begin
  aWaitingTimes := TList<TStringStatisticPair>.Create;
  aArrivalCounts := TList<TStringStatisticPair>.Create;
end;


procedure TReplicationStatistic.Clear;
begin
  aWaitingTimes.Clear;
  aArrivalCounts.Clear;
end;

destructor TReplicationStatistic.Destroy;
begin
 aWaitingTimes.Free;
 aArrivalCounts.Free;
end;

procedure TReplicationStatistic.LoadFromXML(paNode: IXMLNode);
var
  node, node1 : IXMLNode;
  stat : TStringStatisticPair;
  sstat : TStringStatisticPair;
  j: Integer;
begin
  node := paNode.ChildNodes['waitings'];
  for j := 0 to node.ChildNodes.Count - 1 do
   begin
     stat := TStringStatisticPair.Create;
     stat.LoadFromXML(node.ChildNodes[j]);
     aWaitingTimes.Add(stat);
   end;

  node := paNode.ChildNodes['arrivals'];
  for j := 0 to node.ChildNodes.Count - 1 do
   begin
     sstat := TStringStatisticPair.Create;
     sstat.LoadFromXML(node.ChildNodes[j]);
     aArrivalCounts.Add(sstat);
   end;

end;

procedure TReplicationStatistic.SaveToXML(var paNode: Ixmlnode);
var
  node, node1 : IXMLNode;
  sstat : TStringStatisticPair;
begin
  node := paNode.AddChild('replication');
  node1 := node.AddChild('waitings');
  for sstat in aWaitingTimes do
    sstat.SaveToXML(node1);

  node1 := Node.AddChild('arrivals');
  for sstat in aArrivalCounts do
    sstat.SaveToXML(node1);

end;

constructor TintegerSimpleStatisticPair.Create;
begin
  aKey := 0;
  aValue := TSimpleStat.create;
end;

destructor TintegerSimpleStatisticPair.Destroy;
begin
  aValue.Free;
end;

procedure TintegerStatisticPair.SaveToXML(var paNode : Ixmlnode);
var
  n1, node : IXMLNode;
begin
  n1 := paNode.AddChild('statistic');
  TSerializer.saveValueToXML(n1, 'key', aKey);

  node := n1.AddChild('value');
  TSerializer.saveValueToXML(node, 'mean', aValue.Mean);
  TSerializer.saveValueToXML(node, 'dispersion', aValue.Dispersion);

end;

procedure TintegerStatisticPair.LoadFromXML(paNode : IXMLNode);
begin
  TSerializer.loadValueFromXML(paNode, 'key', aKey);
  TSerializer.loadValueFromXML(paNode.ChildNodes['value'], 'mean', aValue.aMean);
  TSerializer.loadValueFromXML(paNode.ChildNodes['value'], 'dispersion',
        aValue.aDispersion);

end;

end.



